home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / programming / other / scm / slib / factor.scm < prev    next >
Text File  |  1999-04-19  |  9KB  |  246 lines

  1. ;;;; "factor.scm" factorization, prime test and generation
  2. ;;; Copyright (C) 1991, 1992, 1993, 1998 Aubrey Jaffer.
  3. ;
  4. ;Permission to copy this software, to redistribute it, and to use it
  5. ;for any purpose is granted, subject to the following restrictions and
  6. ;understandings.
  7. ;
  8. ;1.  Any copy made of this software must include this copyright notice
  9. ;in full.
  10. ;
  11. ;2.  I have made no warrantee or representation that the operation of
  12. ;this software will be error-free, and I am under no obligation to
  13. ;provide any services, by way of maintenance, update, or otherwise.
  14. ;
  15. ;3.  In conjunction with products arising from the use of this
  16. ;material, there shall be no use of my name in any advertising,
  17. ;promotional, or sales literature without prior written consent in
  18. ;each case.
  19.  
  20. (require 'common-list-functions)
  21. (require 'modular)
  22. (require 'random)
  23. (require 'byte)
  24.  
  25. ;;@body
  26. ;; @0 is the random-state (@pxref{Random Numbers}) used by these
  27. ;; procedures.  If you call these procedures from more than one thread
  28. ;; (or from interrupt), @code{random} may complain about reentrant
  29. ;; calls.
  30. (define prime:prngs
  31.   (make-random-state "repeatable seed for primes"))
  32.  
  33.  
  34. ;; @emph{Note:} The prime test and generation procedures implement (or
  35. ;; use) the Solovay-Strassen primality test. See
  36. ;;
  37. ;; @itemize @bullet
  38. ;; @item Robert Solovay and Volker Strassen,
  39. ;; @cite{A Fast Monte-Carlo Test for Primality},
  40. ;; SIAM Journal on Computing, 1977, pp 84-85.
  41. ;; @end itemize
  42.  
  43. ;;; Solovay-Strassen Prime Test
  44. ;;;   if n is prime, then J(a,n) is congruent mod n to a**((n-1)/2)
  45.  
  46. ;;; (modulo p 16) is because we care only about the low order bits.
  47. ;;; The odd? tests are inline of (expt -1 ...)
  48.  
  49. (define (prime:jacobi-symbol p q)
  50.   (cond ((zero? p) 0)
  51.     ((= 1 p) 1)
  52.     ((odd? p)
  53.      (if (odd? (quotient (* (- (modulo p 16) 1) (- q 1)) 4))
  54.          (- (prime:jacobi-symbol (modulo q p) p))
  55.          (prime:jacobi-symbol (modulo q p) p)))
  56.     (else
  57.      (let ((qq (modulo q 16)))
  58.        (if (odd? (quotient (- (* qq qq) 1) 8))
  59.            (- (prime:jacobi-symbol (quotient p 2) q))
  60.            (prime:jacobi-symbol (quotient p 2) q))))))
  61. ;;@args p q
  62. ;; Returns the value (+1, @minus{}1, or 0) of the Jacobi-Symbol of
  63. ;; exact non-negative integer @1 and exact positive odd integer @2.
  64. (define jacobi-symbol prime:jacobi-symbol)
  65.  
  66. ;;@body
  67. ;; @0 the maxinum number of iterations of Solovay-Strassen that will
  68. ;; be done to test a number for primality.
  69. (define prime:trials 30)
  70.  
  71. ;;; checks if n is prime.  Returns #f if not prime. #t if (probably) prime.
  72. ;;;   probability of a mistake = (expt 2 (- prime:trials))
  73. ;;;     choosing prime:trials=30 should be enough
  74. (define (Solovay-Strassen-prime? n)
  75.   (do ((i prime:trials (- i 1))
  76.        (a (+ 2 (random (- n 2) prime:prngs))
  77.       (+ 2 (random (- n 2) prime:prngs))))
  78.       ((not (and (positive? i)
  79.          (= (gcd a n) 1)
  80.          (= (modulo (prime:jacobi-symbol a n) n)
  81.             (modular:expt n a (quotient (- n 1) 2)))))
  82.        (if (positive? i) #f #t))))
  83.  
  84. ;;; prime:products are products of small primes.
  85. (define (primes-gcd? n comps)
  86.   (comlist:notevery (lambda (prd) (= 1 (gcd n prd))) comps))
  87. (define prime:prime-sqr 121)
  88. (define prime:products '(105))
  89. (define prime:sieve (bytes 0 0 1 1 0 1 0 1 0 0 0))
  90. (letrec ((lp (lambda (comp comps primes nexp)
  91.            (cond ((< comp (quotient most-positive-fixnum nexp))
  92.               (let ((ncomp (* nexp comp)))
  93.             (lp ncomp comps
  94.                 (cons nexp primes)
  95.                 (next-prime nexp (cons ncomp comps)))))
  96.              ((< (quotient comp nexp) (* nexp nexp))
  97.               (set! prime:prime-sqr (* nexp nexp))
  98.               (set! prime:sieve (make-bytes nexp 0))
  99.               (for-each (lambda (prime)
  100.                   (byte-set! prime:sieve prime 1))
  101.                 primes)
  102.               (set! prime:products (reverse (cons comp comps))))
  103.              (else
  104.               (lp nexp (cons comp comps)
  105.               (cons nexp primes)
  106.               (next-prime nexp (cons comp comps)))))))
  107.      (next-prime (lambda (nexp comps)
  108.                (set! comps (reverse comps))
  109.                (do ((nexp (+ 2 nexp) (+ 2 nexp)))
  110.                ((not (primes-gcd? nexp comps)) nexp)))))
  111.   (lp 3 '() '(2 3) 5))
  112.  
  113. (define (prime:prime? n)
  114.   (set! n (abs n))
  115.   (cond ((< n (bytes-length prime:sieve)) (positive? (byte-ref prime:sieve n)))
  116.     ((even? n) #f)
  117.     ((primes-gcd? n prime:products) #f)
  118.     ((< n prime:prime-sqr) #t)
  119.     (else (Solovay-Strassen-prime? n))))
  120. ;;@args n
  121. ;; Returns @code{#f} if @1 is composite; @code{#t} if @1 is prime.
  122. ;; There is a slight chance @code{(expt 2 (- prime:trials))} that a
  123. ;; composite will return @code{#t}.
  124. (define prime? prime:prime?)
  125. (define probably-prime? prime:prime?)    ;legacy
  126.  
  127. (define (prime:prime< start)
  128.   (do ((nbr (+ -1 start) (+ -1 nbr)))
  129.       ((or (negative? nbr) (prime:prime? nbr))
  130.        (if (negative? nbr) #f nbr))))
  131.  
  132. (define (prime:primes< start count)
  133.   (do ((cnt (+ -2 count) (+ -1 cnt))
  134.        (lst '() (cons prime lst))
  135.        (prime (prime:prime< start) (prime:prime< prime)))
  136.       ((or (not prime) (negative? cnt))
  137.        (if prime (cons prime lst) lst))))
  138. ;;@args start count
  139. ;; Returns a list of the first @2 prime numbers less than
  140. ;; @1.  If there are fewer than @var{count} prime numbers
  141. ;; less than @var{start}, then the returned list will have fewer than
  142. ;; @var{start} elements.
  143. (define primes< prime:primes<)
  144.  
  145. (define (prime:prime> start)
  146.   (do ((nbr (+ 1 start) (+ 1 nbr)))
  147.       ((prime:prime? nbr) nbr)))
  148.  
  149. (define (prime:primes> start count)
  150.   (set! start (max 0 start))
  151.   (do ((cnt (+ -2 count) (+ -1 cnt))
  152.        (lst '() (cons prime lst))
  153.        (prime (prime:prime> start) (prime:prime> prime)))
  154.       ((negative? cnt)
  155.        (reverse (cons prime lst)))))
  156. ;;@args start count
  157. ;; Returns a list of the first @2 prime numbers greater than @1.
  158. (define primes> prime:primes>)
  159.  
  160. ;;;;Lankinen's recursive factoring algorithm:
  161. ;From: ld231782@longs.LANCE.ColoState.EDU (L. Detweiler)
  162.  
  163. ;                  |  undefined if n<0,
  164. ;                  |  (u,v) if n=0,
  165. ;Let f(u,v,b,n) := | [otherwise]
  166. ;                  |  f(u+b,v,2b,(n-v)/2) or f(u,v+b,2b,(n-u)/2) if n odd
  167. ;                  |  f(u,v,2b,n/2) or f(u+b,v+b,2b,(n-u-v-b)/2) if n even
  168.  
  169. ;Thm: f(1,1,2,(m-1)/2) = (p,q) iff pq=m for odd m.
  170.  
  171. ;It may be illuminating to consider the relation of the Lankinen function in
  172. ;a `computational hierarchy' of other factoring functions.*  Assumptions are
  173. ;made herein on the basis of conventional digital (binary) computers.  Also,
  174. ;complexity orders are given for the worst case scenarios (when the number to
  175. ;be factored is prime).  However, all algorithms would probably perform to
  176. ;the same constant multiple of the given orders for complete composite
  177. ;factorizations.
  178.  
  179. ;Thm: Eratosthenes' Sieve is very roughtly O(ln(n)/n) in time and
  180. ;     O(n*log2(n)) in space.
  181. ;Pf: It works with all prime factors less than n (about ln(n)/n by the prime
  182. ;    number thm), requiring an array of size proportional to n with log2(n)
  183. ;    space for each entry.
  184.  
  185. ;Thm: `Odd factors' is O((sqrt(n)/2)*log2(n)) in time and O(log2(n)) in
  186. ;     space.
  187. ;Pf: It tests all odd factors less than the square root of n (about
  188. ;    sqrt(n)/2), with log2(n) time for each division.  It requires only
  189. ;    log2(n) space for the number and divisors.
  190.  
  191. ;Thm: Lankinen's algorithm is O(sqrt(n)/2) in time and O((sqrt(n)/2)*log2(n))
  192. ;     in space.
  193. ;Pf: The algorithm is easily modified to seach only for factors p<q for all
  194. ;    pq=m.  Then the recursive call tree forms a geometric progression
  195. ;    starting at one, and doubling until reaching sqrt(n)/2, or a length of
  196. ;    log2(sqrt(n)/2).  From the formula for a geometric progression, there is
  197. ;    a total of about 2^log2(sqrt(n)/2) = sqrt(n)/2 calls.  Assuming that
  198. ;    addition, subtraction, comparison, and multiplication/division by two
  199. ;    occur in constant time, this implies O(sqrt(n)/2) time and a
  200. ;    O((sqrt(n)/2)*log2(n)) requirement of stack space.
  201.  
  202. (define (prime:f u v b n)
  203.   (if (<= n 0)
  204.       (cond ((negative? n) #f)
  205.         ((= u 1) #f)
  206.         ((= v 1) #f)
  207.         ; Do both of these factors need to be factored?
  208.         (else (append (or (prime:f 1 1 2 (quotient (- u 1) 2))
  209.                   (list u))
  210.               (or (prime:f 1 1 2 (quotient (- v 1) 2))
  211.                   (list v)))))
  212.       (if (even? n)
  213.       (or (prime:f u v (+ b b) (quotient n 2))
  214.           (prime:f (+ u b) (+ v b) (+ b b) (quotient (- n (+ u v b)) 2)))
  215.       (or (prime:f (+ u b) v (+ b b) (quotient (- n v) 2))
  216.           (prime:f u (+ v b) (+ b b) (quotient (- n u) 2))))))
  217.  
  218. (define (prime:fo m)
  219.   (let* ((s (gcd m (car prime:products)))
  220.      (r (quotient m s)))
  221.     (if (= 1 s)
  222.     (or (prime:f 1 1 2 (quotient (- m 1) 2)) (list m))
  223.     (append
  224.      (if (= 1 r) '()
  225.          (or (prime:f 1 1 2 (quotient (- r 1) 2)) (list r)))
  226.      (or (prime:f 1 1 2 (quotient (- s 1) 2)) (list s))))))
  227.  
  228. (define (prime:fe m)
  229.   (if (even? m)
  230.       (cons 2 (prime:fe (quotient m 2)))
  231.       (if (eqv? 1 m)
  232.       '()
  233.       (prime:fo m))))
  234.  
  235. (define (prime:factor k)
  236.   (case k
  237.     ((-1 0 1) (list k))
  238.     (else (if (negative? k)
  239.           (cons -1 (prime:fe (- k)))
  240.           (prime:fe k)))))
  241. ;;@args k
  242. ;; Returns a list of the prime factors of @1.  The order of the
  243. ;; factors is unspecified.  In order to obtain a sorted list do
  244. ;; @code{(sort! (factor @var{k}) <)}.
  245. (define factor prime:factor)
  246.